package dpss.frontend;

import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import org.omg.CORBA.ORB;
import org.omg.CORBA.ShortHolder;
import org.omg.CORBA.StringHolder;

import dpss.commons.CustomLogger;
import dpss.commons.UDPMessage;
import dpss_api_corba.DpssApiPOA;
import dpss_api_corba.DpssApiPackage.DpssException;

/**
 * 
 * The CORBA implementation of calls coming from the Frontend
 * 
 * @author Shewetaank Indora
 * 
 */
public class DpssApiImpl extends DpssApiPOA {
	
	private static CustomLogger logger = CustomLogger.getInstance();

	private ORB orb;
	private HashMap<String, InetSocketAddress> leaderSocketsAddresses;
	private DatagramSocket socket;
	private List<UDPMessage> dpssResponseCache;
	

	/**
	 * Constructor
	 * @param socket
	 * @param leaderSocketAddresses
	 */
	public DpssApiImpl(DatagramSocket socket,
			HashMap<String, InetSocketAddress> leaderSocketAddresses) {
		this.leaderSocketsAddresses = leaderSocketAddresses;
		this.socket = socket;
		dpssResponseCache = Collections.synchronizedList( new ArrayList<UDPMessage>() );
		new UDPListener(socket, dpssResponseCache).start();
	}


	/**
	 * Gets the leader socket address from the subnet provided
	 * 
	 * @param subnet
	 * @return
	 */
	private InetSocketAddress getLeaderSocketAddress(String clientIp) {
		
		String[] tokens = clientIp.split("\\.");
		switch (tokens[0]){
		case "182": return leaderSocketsAddresses.get("asia");
		case "132": return leaderSocketsAddresses.get("na");
		case "93": return leaderSocketsAddresses.get("eur");
		}

		return null;
	}

	/**
	 * ORB setter
	 * 
	 * @param orb
	 */
	public void setORB(ORB orb) {
		this.orb = orb;
	}


	@Override
	/**
	 * PLAYER SIGN IN REQUEST
	 */
	public void playerSignIn(String username, String password,
			final String ipAddress, final StringHolder firstName, final StringHolder lastName,
			final ShortHolder age) throws DpssException {

		/****************************
		 * CORBA TO UDP CONVERSION...
		 ****************************/
		HashMap<String, String> requestData = new HashMap<String, String>();
		requestData.put("username", username);
		requestData.put("password", password);
		requestData.put("ipAddress", ipAddress);
		UDPMessage request = new UDPMessage("dpss_playerSignIn", requestData, true, null);
		
		// Set the request ID so we can look for the response of this request
		request.setRequestId(UUID.randomUUID());
		
		// Send the request to the leader
		logger.log("Forwarding playerSignIn request from " + ipAddress + " to leader: " + getLeaderSocketAddress(ipAddress) + " and then waiting for response...");
		UDPMessage.sendMessage(socket, request, getLeaderSocketAddress(ipAddress));
		
		// Check if we have a response available for our request in the cache
		while(true){
			synchronized (dpssResponseCache){
				for (UDPMessage response : dpssResponseCache){
					if (response.getResponseId().equals(response.getResponseId())){ // Response found

						logger.log("Response received for "+ipAddress+", now processing it");
						dpssResponseCache.remove(response);
						
						// This is the response we were looking for
						if (response.isSuccess()){
							
							/******************
							 * UDP TO CORBA...
							 ******************/
							HashMap<String, String> responseData = (HashMap<String, String>) response.getData();
							firstName.value = responseData.get("firstName");
							lastName.value = responseData.get("lastName");
							age.value = Short.parseShort(responseData.get("age"));
		
							logger.log("Responded to " + ipAddress + " with title: " + response.getMessage());
							return;
							
						} else {
							logger.log("Exception thrown to " + ipAddress + ", reason: " + response.getErrorMessage());
							throw new DpssException(response.getErrorMessage());
						}
					}
				}
			}
		}
	}

	
	
	
	
	
	
	
	
	
	

	@Override
	/**
	 * PLAYER SIGN OUT
	 */
	public void playerSignOut(String username, String ipAddress)
			throws DpssException {
		
		
		/****************************
		 * CORBA TO UDP CONVERSION...
		 ****************************/
		HashMap<String, String> requestData = new HashMap<String, String>();
		requestData.put("username", username);
		requestData.put("ipAddress", ipAddress);
		UDPMessage request = new UDPMessage("dpss_playerSignOut", requestData, true, null);
		
		// Set the request ID so we can look for the response of this request
		request.setRequestId(UUID.randomUUID());
		
		// Send the request to the leader
		logger.log(ipAddress);
		logger.log("Forwarding playerSignOut request from " + ipAddress + " to leader: " + getLeaderSocketAddress(ipAddress) + " and then waiting for response...");
		UDPMessage.sendMessage(socket, request, getLeaderSocketAddress(ipAddress));
		
		// Check if we have a response available for our request in the cache
		while(true){
			synchronized (dpssResponseCache){
				for (UDPMessage response : dpssResponseCache){
					if (response.getResponseId().equals(response.getResponseId())){ // Response found
						
						logger.log("Response received for "+ipAddress+", now processing it");
						dpssResponseCache.remove(response);
						
						// This is the response we were looking for
						if (response.isSuccess()){
	
							/******************
							 * UDP TO CORBA...
							 ******************/
							// No values to send for this method
		
							logger.log("Responded to " + ipAddress + " with title: " + response.getMessage());
							return;
							
						} else {
							logger.log("Exception thrown to " + ipAddress + ", reason: " + response.getErrorMessage());
							throw new DpssException(response.getErrorMessage());
						}
					}
				}
			}
		}
		
		
		
		
	}


	@Override
	/**
	 * TRANSFER PLAYER ACCOUNT
	 */
	public void transferAccount(String username, String password,
			String oldIpAddress, String newIpAddress) throws DpssException {


		
		/****************************
		 * CORBA TO UDP CONVERSION...
		 ****************************/
		HashMap<String, String> requestData = new HashMap<String, String>();
		requestData.put("username", username);
		requestData.put("password", password);
		requestData.put("oldIpAddress", oldIpAddress);
		requestData.put("newIpAddress", newIpAddress);
		UDPMessage request = new UDPMessage("dpss_transferAccount", requestData, true, null);
		
		// Set the request ID so we can look for the response of this request
		request.setRequestId(UUID.randomUUID());
		
		// Send the request to the leader
		logger.log("Forwarding transferAccount request from " + oldIpAddress + " to leader: " + getLeaderSocketAddress(oldIpAddress) + " and then waiting for response...");
		UDPMessage.sendMessage(socket, request, getLeaderSocketAddress(oldIpAddress));
		
		// Check if we have a response available for our request in the cache
		while(true){
			synchronized (dpssResponseCache){
				for (UDPMessage response : dpssResponseCache){
					if (response.getResponseId().equals(response.getResponseId())){ // Response found
						
						logger.log("Response received for "+oldIpAddress+", now processing it");
						dpssResponseCache.remove(response);
						
						// This is the response we were looking for
						if (response.isSuccess()){
	
		
							/******************
							 * UDP TO CORBA...
							 ******************/
							// No values to set
		
							logger.log("Responded to " + oldIpAddress + " with title: " + response.getMessage());
							return;
							
						} else {
							logger.log("Exception thrown to " + oldIpAddress + ", reason: " + response.getErrorMessage());
							throw new DpssException(response.getErrorMessage());
						}
					}
				}
			}
		}
	}


	@Override
	public void getPlayersStatus(String adminUsername, String adminPassword,
			String ipAddress, StringHolder playersStatus) throws DpssException {
		
		
		/****************************
		 * CORBA TO UDP CONVERSION...
		 ****************************/
		HashMap<String, String> requestData = new HashMap<String, String>();
		requestData.put("adminUsername", adminUsername);
		requestData.put("adminPassword", adminPassword);
		requestData.put("ipAddress", ipAddress);
		UDPMessage request = new UDPMessage("dpss_getPlayersStatus", requestData, true, null);
		
		// Set the request ID so we can look for the response of this request
		request.setRequestId(UUID.randomUUID());
		
		// Send the request to the leader
		logger.log("Forwarding getPlayersStatus request from " + ipAddress + " to leader: " + getLeaderSocketAddress(ipAddress) + " and then waiting for response...");
		UDPMessage.sendMessage(socket, request, getLeaderSocketAddress(ipAddress));
		
		// Check if we have a response available for our request in the cache
		while(true){
			synchronized (dpssResponseCache){
				for (UDPMessage response : dpssResponseCache){
					if (response.getResponseId().equals(response.getResponseId())){ // Response found
						
						logger.log("Response received for "+ipAddress+", now processing it");
						dpssResponseCache.remove(response);
						
						// This is the response we were looking for
						if (response.isSuccess()){
	

							/******************
							 * UDP TO CORBA...
							 ******************/
							playersStatus = (StringHolder) response.getData();
		
							logger.log("Responded to " + ipAddress + " with title: " + response.getMessage());
							return;
							
						} else {
							logger.log("Exception thrown to " + ipAddress + ", reason: " + response.getErrorMessage());
							throw new DpssException(response.getErrorMessage());
						}
					}
				}
			}
		}
	}


	@Override
	/**
	 * SUSPENDS A PLAYER ACCOUNT
	 */
	public void suspendAccount(String adminUsername, String adminPassword,
			String ipAddress, String usernameToSuspend) throws DpssException {
		
		/****************************
		 * CORBA TO UDP CONVERSION...
		 ****************************/
		HashMap<String, String> requestData = new HashMap<String, String>();
		requestData.put("adminUsername", adminUsername);
		requestData.put("adminPassword", adminPassword);
		requestData.put("ipAddress", ipAddress);
		requestData.put("usernameToSuspend", usernameToSuspend);
		UDPMessage request = new UDPMessage("dpss_suspendAccount", requestData, true, null);
		
		// Set the request ID so we can look for the response of this request
		request.setRequestId(UUID.randomUUID());
		
		// Send the request to the leader
		logger.log("Forwarding suspendAccount request from " + ipAddress + " to leader: " + getLeaderSocketAddress(ipAddress) + " and then waiting for response...");
		UDPMessage.sendMessage(socket, request, getLeaderSocketAddress(ipAddress));
		
		// Check if we have a response available for our request in the cache
		while(true){
			synchronized (dpssResponseCache){
				for (UDPMessage response : dpssResponseCache){
					if (response.getResponseId().equals(response.getResponseId())){ // Response found
						
						logger.log("Response received for "+ipAddress+", now processing it");
						dpssResponseCache.remove(response);
						
						// This is the response we were looking for
						if (response.isSuccess()){
	

							/******************
							 * UDP TO CORBA...
							 ******************/
							// No values to set for this method
		
							logger.log("Responded to " + ipAddress + " with title: " + response.getMessage());
							return;
							
						} else {
							logger.log("Exception thrown to " + ipAddress + ", reason: " + response.getErrorMessage());
							throw new DpssException(response.getErrorMessage());
						}
					}
				}
			}
		}
		
		
	}


	@Override
	/**
	 * CREATE PLAYER ACCOUNT
	 */
	public void createPlayerAccount(String firstName, String lastName,
			short age, String username, String password, String ipAddress)
			throws DpssException {
		
		/****************************
		 * CORBA TO UDP CONVERSION...
		 ****************************/
		HashMap<String, String> requestData = new HashMap<String, String>();
		requestData.put("firstName", firstName);
		requestData.put("lastName", lastName);
		requestData.put("age", Short.toString(age));
		requestData.put("username", username);
		requestData.put("password", password);
		requestData.put("ipAddress", ipAddress);
		UDPMessage request = new UDPMessage("dpss_createPlayerAccount", requestData, true, null);
		
		// Set the request ID so we can look for the response of this request
		request.setRequestId(UUID.randomUUID());
		
		// Send the request to the leader
		logger.log("Forwarding createPlayerAccount request from " + ipAddress + " to leader: " + getLeaderSocketAddress(ipAddress) + " and then waiting for response...");
		UDPMessage.sendMessage(socket, request, getLeaderSocketAddress(ipAddress));
		
		// Check if we have a response available for our request in the cache
		while(true){
			synchronized (dpssResponseCache){
				for (UDPMessage response : dpssResponseCache){
					if (response.getResponseId().equals(response.getResponseId())){ // Response found
						
						logger.log("Response received for "+ipAddress+", now processing it");
						dpssResponseCache.remove(response);
						
						// This is the response we were looking for
						if (response.isSuccess()){
							
							/******************
							 * UDP TO CORBA...
							 ******************/
							// No values to set for this method
		
							logger.log("Responded to " + ipAddress + " with title: " + response.getMessage());
							return;
							
						} else {
							logger.log("Exception thrown to " + ipAddress + ", reason: " + response.getErrorMessage());
							throw new DpssException(response.getErrorMessage());
						}
					}
				}
			}
		}
		
	}



}
